iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Software Development

30天學會Golang系列 第 9

Day09 - Go的單元測試 (上)

  • 分享至 

  • xImage
  •  

單元測試

好的程式碼除了有好的錯誤處理以外(day08錯誤處理),測試也是很重要的一環,我們除了透過之前將結果輸出查看結果以外,也能透過單元測試來確認我們函數的正確性,此外單元測試也能看出函數的效能,我們知道單元測試的重點在於發現程式設計或實現的邏輯錯誤,使問題及早暴露,便於問題的定位解決。本小節將帶著這一連串的問題來講解 Go 語言中如何來實現單元測試和效能測試。本篇內容主要參考下面的參考來源1參考來源2,其中用 go 做單元測試時,有以下6個規則:

    1. 檔名必須是_test.go結尾的,這樣在執行go test的時候才會執行到相應的程式碼
    1. 必須 import testing這個包
    1. 所有的測試案例函式必須是 Test 開頭
    1. 測試函式TestXxx()的參數是testing.T
    1. 測試格式:func TestXxx (t *testing.T),Xxx部分可以為任意的字母數字的組合,但是首字母不能是小寫字母。例如 Testbubblesort 是錯誤的函式名,至少是 TestBubblesort 或 TestBubbleSort,當然比較推薦 TestBubbleSort。
    1. 函式中透過呼叫testing.T的Error, Errorf, FailNow, Fatal, FatalIf方法,說明測試不透過,呼叫 Log 方法用來記錄測試的資訊。

我們以參考來源1的內容為例,主要是針對 BubbleSort 來做測試

func BubbleSort(numbers []int) []int {
	unsortedUntilIndex := len(numbers) - 1
	swap := true

	for swap {
		swap = false
		for i := 0; i < unsortedUntilIndex; i++ {
			first := numbers[i]
			second := numbers[i+1]

			if first > second {
				numbers[i] = second
				numbers[i+1] = first
				swap = true
			}
		}
		unsortedUntilIndex--
	}

	return numbers
}

func Equals(t *testing.T, module string, given string, should string, result interface{}, expected interface{}) {
	message := createTestMessage(module, given, should, result, expected)

	if reflect.DeepEqual(result, expected) {
		// fmt.Print(message)
	} else {
		t.Errorf(message)
	}
}

// 自定義輸出格式
func createTestMessage(module string, given string, should string, result interface{}, expected interface{}) string {
	return fmt.Sprintf(`
%v
given:    %v
should:   %v
result:   %v
expected: %v
	`, module, given, should, result, expected)
}

// 單元測試,命名方式請遵守上面的6個規則,表格驅動測試
func TestBubbleSort(t *testing.T) {
	tests := []struct {
		input    []int
		expected []int
	}{
		{[]int{4, 2, 7, 1, 3}, []int{1, 2, 3, 4, 7}},
		{[]int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
	}

	for _, test := range tests {
		result := BubbleSort(test.input)
		Equals(t, "BubbleSort()", "Given slice of integers", "Should return slice sorted in ascending order", result, test.expected)
	}
}

其中可以看到在 TestBubbleSort 裡面有用 []struct{}{} 包裝要測試的資料,這種作法叫做表格驅動測試,當測試的函數相同時,我們可以用這種易讀的方式編寫我們的測試代碼,尤其是當想測試很多不同的情境時,這種編寫方式的優勢就會被體現出來,以下是原始寫法,可以比較原始寫法與表格驅動的寫法的差異

// 原始測試寫法
func TestBubbleSort(t *testing.T) {
	result := BubbleSort([]int{4, 2, 7, 1, 3})
	expected := []int{1, 2, 3, 4, 7}
	Equals(t, "BubbleSort()", "Given slice of integers", "Should return slice sorted in ascending order", result, expected)

	result = BubbleSort([]int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1})
	expected = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	Equals(t, "BubbleSort()", "Given slice of integers", "Should return slice sorted in ascending order", result, expected)
}

在該檔案的路徑下,終端機輸入 go test 就可以看到以下結果:

PASS
ok      it/day09        0.319s
第9天報到,這東西說實在我還沒體會到他的好處,我感覺把東西print出來也很直覺,不曉得為什麼大家會這麼推單元測試~我還太淺

參考來源

  1. https://iulspop.medium.com/golang-setup-for-unit-testing-debugging-with-vs-code-ae02ba3e5024
  2. https://willh.gitbook.io/build-web-application-with-golang-zhtw/11.0/11.3

代碼連結

https://github.com/luckyuho/ithome30-golang/tree/main/day09


上一篇
Day08 - Go的錯誤處理(error handling)
下一篇
Day10 - Go的單元測試 (下)
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言